/**
 * Created Date: Tuesday, November 29th 2022, 11:54:44 pm
 * Author: CodingGorit
 * -----
 * Last Modified: Sat Mar 18 2023
 * Modified By: CodingGorit
 * -----
 * Copyright (c) 2022 fmin-courses
 * ------------------------------------
 * Javascript will save your soul!
 */

"use strict";
import EventEmitter from "events";
import Utils from '../utils/Utils';
import log from '../utils/log';
import ValidatorUtils from "../utils/ValidatorUtils";
const nodemailer = require("nodemailer");
const TAG = "MailManager";

const emailWhiteList = ["Your email white list"];

export interface IMailMessage {
    from: string,   // emaill sender's address  aaa@example.com
    to: string | string[],     // 收件人 字符串用 用逗号隔开 或者数组分离
    cc?: string | string[],     // 抄送
    bcc?: string | string[],    // 密件抄送
    subject: string,    // 主题
    text: string,   // 消息文本
    html: string,  // html文本，定义了 html，消息就会忽略
    attachments?: any,   // 附件内容
}

export interface IEmailTransport {
    host: string,
    port: number,
    secure: boolean,
    auth: {
        user: string,   // 邮箱名称
        pass: string,   // 授权码
    },
}

export enum MailMessageCodeEnum {
    SUCCESS = 1,
    FAIL = 0,
    ERROR = -1
}

// 邮件管理类
class MailManager extends EventEmitter {
    private static _mailManager: MailManager;

    public static getInstance() {
        if (!this._mailManager) {
            this._mailManager = new MailManager();
        }
        return this._mailManager;
    }

    private constructor() {
        super();
    }

    /**
     * 
     * @param to email recivers
     * @param config node mail config
     * @param callback response
     */
    public sendVerifyCode(to: string[], config: IEmailTransport, callback: (err: number, reason: string) => void) {
        if (!to || to.length === 0) {
            callback(MailMessageCodeEnum.FAIL, "mail is null");
        }

        const res = to.every(item => {
            return ValidatorUtils.isValidMaill(item);
        });

        if (!res) {
            callback(MailMessageCodeEnum.FAIL, "mail is invalid");
        }
        try {
            if (!Utils.isObjectValid(config)) {
                config = Utils.getInstance().getConfig("mail_config.defaultTransporter") as IEmailTransport;
            }
            if (config) {
                const transport = nodemailer.createTransport(config);

                // 验证邮箱服务可行
                transport.verify(async function (error, success) {
                    if (error) {
                        log.error(`${TAG} error is => ${error}`);
                        callback(MailMessageCodeEnum.FAIL, error);
                    } else {
                        const mailOption: IMailMessage = {
                            from: `"Verify Code 👻" <${config.auth.user}>`, // sender address
                            to: [...to],
                            subject: "Get Your verify code",
                            text: "Plaintext version of the message",
                            html: `<p>Your verify code is: <b><font color="red">${Utils.getInstance().getRandomVerfiyCode(8)}</font></b> , It will expire after 15 minutes, Do not tell anyone!!!</p>`
                        }

                        let data = await transport.sendMail(mailOption);
                        callback(MailMessageCodeEnum.SUCCESS, data);
                    }
                });
            } else {
                log.error(`${TAG} registerMailManager config is null`);
                callback(MailMessageCodeEnum.FAIL, "config is null");
            }
        } catch (err: any) {
            log.error(`${TAG} sendMail error ${err}`);
            callback(MailMessageCodeEnum.ERROR, err);
        }
    }

    // 发送自定义 email（发件人固定）
    public sendCustomMail(message: IMailMessage, callback: (err: number, reason: string, response?: any) => void) {
        if (!message) {
            callback(0, "email message is invalid");
        }
        const config = Utils.getInstance().getConfig<IEmailTransport>("mail.transport") as IEmailTransport;
        this.sendMessageTemplate(config, message, callback);
    }

    private sendMessageTemplate(config: IEmailTransport, message: IMailMessage, callback: (err: number, reason: string, response?: any) => void) {
        if (!config || !message) {
            callback(MailMessageCodeEnum.FAIL, "config or message invalid");
        }

        const isMessageValid = this.checkMailMessageValid(message);
        if (!isMessageValid.result) {
            callback(MailMessageCodeEnum.FAIL, isMessageValid.reason);
        }

        try {
            const transport = nodemailer.createTransport(config);
            transport.verify(async function (error, success) {
                if (error) {
                    log.error(`${TAG} error is => ${error}`);
                    callback(MailMessageCodeEnum.FAIL, error);   
                } else if (success) {
                    if (!message.from.match(config.auth.user)) {
                        log.debug(`${TAG} mail sender not match, rewrite it !`);
                        message.from = `tech 👻 <${config.auth.user}>`;
                    };
                    const mailOption: IMailMessage = {...message};
                    let data = await transport.sendMail(mailOption);
                    log.info(`${TAG}, data is => ${JSON.stringify(data)}`);
                    callback(MailMessageCodeEnum.SUCCESS, "send mail successfully", data);
                }
            });
        } catch (e: any) {
            log.error(`${TAG} sendMessageTemplate err => ${e}`);
            callback(MailMessageCodeEnum.FAIL, e);
        }
    }

    private checkMailMessageValid (message: IMailMessage): {result: boolean, reason: string} {
        const res = {
            result: true,
            reason: 'message in valid'
        };

        if (!message) {
            res.result = false;
            res.reason = "message is invalid";
        } 

        // 配置中心读取
        if (!message.from || !ValidatorUtils.isValidMaill(message.from)) {
            log.info(`${TAG} from ${message.from}`);
                
            res.result = false;
            res.reason = "sender's email foramt is invalid";
        } else {
            res.result = true;
        }

        // recivers' email
        if (message.to) {
            const reciversArray = Array.from(message.to) as unknown as Array<string>;
            const isReciversMailValid = reciversArray.every((item: string) => {
                return ValidatorUtils.isValidMaill(item);
            });
            if (!isReciversMailValid) {
                res.result = false;
                res.reason = "recivers' email is invliad"
            }
        }
        
        if (!message.subject || message.subject.length === 0) {
            res.reason = "The subject is not allowed to be empty";
            res.result = false;
        }

        // text 内容可以为空，html 不为空   
        if ((!message.text || message.text.length === 0) && !message.html) {
            res.reason = 'html or text is null';
            res.result = false;
        }
        return res;
    }
    public destoryMailManager() {
        MailManager._mailManager.removeAllListeners();
    }
}


export default MailManager;